#include <stdlib.h>
#include <iostream>
#include "Arbre.h"
#include "Symbole.h"
#include "SymboleValue.h"
#include "Erreur.h"
#include <typeinfo>

Noeud::Noeud(int ligne, int colonne) { this->ligne = ligne; this->colonne = colonne; }


////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
NoeudSeqInst::NoeudSeqInst(int ligne, int colonne) : Noeud(ligne, colonne), tabInst() {
}

////////////////////////////////////////////////////////////////////////////////
int NoeudSeqInst::getValeur() {
  int valeur=0;
  for (unsigned int i=0; i<tabInst.size(); i++)
    valeur = tabInst[i]->getValeur();  // on evalue chaque instruction de la séquence
  return valeur; // par convention, resultat = valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
  for (unsigned int i=0; i<tabInst.size(); i++)
    tabInst[i]->afficher(indentation+1); // on affiche les fils en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
  tabInst.push_back(instruction);
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::afficherPhp() {
	for (unsigned int i=0; i<tabInst.size(); i++) {
	    tabInst[i]->afficherPhp();
	    //cout << typeid(tabInst[i]).name() << " == " << typeid(tabInst[i]).name() << endl;
	    if(typeid(*tabInst[i]) ==  typeid(NoeudAffectation)) { 
	    	cout << ";" << endl;
	    }
	}
}

////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
NoeudAffectation::NoeudAffectation(int ligne, int colonne, Noeud* variable, Noeud* expression) : Noeud(ligne, colonne) {
  this->variable=variable;
  this->expression=expression;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudAffectation::getValeur() {
  int valeur = expression->getValeur(); // on évalue l'expression
  ((SymboleValue*)variable)->setValeur(valeur); // on affecte la variable
  return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
void NoeudAffectation::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Affectation" << endl;
  variable->afficher(indentation+1);   // on affiche variable et expression
  expression->afficher(indentation+1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
void NoeudAffectation::afficherPhp(){
	variable->afficherPhp();
	cout  << " = ";
	expression->afficherPhp();
}
////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBinaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBinaire::NoeudOperateurBinaire ( int ligne, int colonne, Symbole operateur, Noeud* operandeGauche, Noeud* operandeDroit) : Noeud(ligne, colonne) {
  this->operateur=operateur;
  this->operandeGauche=operandeGauche;
  this->operandeDroit=operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudOperateurBinaire::getValeur() {
  int valeur=0;
  int og=operandeGauche->getValeur();
  int od=operandeDroit->getValeur();
  if      (this->operateur=="+") valeur = og+od;
  else if (this->operateur=="-") valeur = og-od;
  else if (this->operateur=="*") valeur = og*od;
  else if (this->operateur=="et") valeur = og&&od;
  else if (this->operateur=="ou") valeur = og||od;
  else if (this->operateur=="==") valeur = og==od;
  else if (this->operateur=="!=") valeur = og!=od;
  else if (this->operateur=="<") valeur = og<od;
  else if (this->operateur=="<=") valeur = og<=od;
  else if (this->operateur==">") valeur = og>od;
  else if (this->operateur==">=") valeur = og>=od;
  else  // this->operateur=="/"
        if (od!=0)
                valeur = og/od;
        else {
		throw Erreur("Division par zéro", this->getLigne(), this->getColonne());
        }
  return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurBinaire::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
  operandeGauche->afficher(indentation+1);  // on affiche fils gauche et fils droit
  operandeDroit->afficher(indentation+1);   // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurBinaire::afficherPhp() {
	operandeGauche->afficherPhp();
	
	if(operateur.getChaine() == "et") {
		cout << "&&";
	} else if(operateur.getChaine() == "ou") {
		cout << "||";
	} else {
		cout << operateur.getChaine();
	}

	operandeDroit->afficherPhp();

}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurUnaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurUnaire::NoeudOperateurUnaire ( int ligne, int colonne, Symbole operateur, Noeud* operande) : Noeud(ligne, colonne) {
  this->operateur=operateur;
  this->operande=operande;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudOperateurUnaire::getValeur() {
  int valeur=0;
  int o=operande->getValeur();
  if      (this->operateur=="non") valeur = !o;
  else // this->operateur=="-"
	valeur = - o;
  return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurUnaire::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Operateur Unaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
  operande->afficher(indentation+1);  // on affiche fils gauche et fils droit
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurUnaire::afficherPhp() {
	if(operateur.getChaine() == "non") {
		cout << "!";
	} else {
		cout << operateur.getChaine();
	}
	operande->afficherPhp();
}

////////////////////////////////////////////////////////////////////////////////
// NoeudBoucle
////////////////////////////////////////////////////////////////////////////////

NoeudBoucle::NoeudBoucle (int ligne, int colonne, Symbole boucle, Noeud* condition, Noeud* instruction) : Noeud(ligne, colonne) {
  this->boucle=boucle;
  this->condition=condition;
  this->instruction=instruction;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudBoucle::getValeur() {
  int valeur=0;
  if(this->boucle=="tantque") {
	while(condition->getValeur()) {
		valeur += instruction->getValeur();
	}
  } else if (this->boucle=="repeter") {
	do {
		valeur += instruction->getValeur();
	} while (condition->getValeur());
  }
  return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudBoucle::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - boucle  applique a : " << endl;
  instruction->afficher(indentation+1);  // on affiche fils gauche et fils droit
}

////////////////////////////////////////////////////////////////////////////////
void NoeudBoucle::afficherPhp(){
	if(this->boucle=="tantque") {
		cout << "while (";
		condition->afficherPhp();
		cout << "){"<<endl;
		instruction->afficherPhp();
		cout << "}" << endl;
	} else if (this->boucle=="repeter") {
		cout << "do {" << endl;
		instruction->afficherPhp();
		cout << "} while (";
		condition->afficherPhp();
		cout << ");" << endl;
  	}
}

////////////////////////////////////////////////////////////////////////////////
// NoeudPour
////////////////////////////////////////////////////////////////////////////////

NoeudPour::NoeudPour(int ligne, int colonne, Noeud* affectation, Noeud* condition, Noeud* incrementation, Noeud* instruction) : Noeud(ligne, colonne) {
  this->affectation = affectation;
  this->condition = condition;
  this->incrementation = incrementation;
  this->instruction=instruction;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudPour::getValeur() {
  int valeur=0;
  for(affectation->getValeur(); condition->getValeur(); incrementation->getValeur()) {
	valeur += instruction->getValeur();
  }
  return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudPour::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "NoeudPour - pour applique a : " << endl;
  instruction->afficher(indentation+1);  // on affiche fils gauche et fils droit
}

////////////////////////////////////////////////////////////////////////////////
void NoeudPour::afficherPhp() {
	cout << "for(";
	affectation->afficherPhp();
	cout << ";";
	condition->afficherPhp();
	cout << ";";
	incrementation->afficherPhp();
	cout << ") {" << endl;
	instruction->afficherPhp();
	cout << "}" << endl;
}
////////////////////////////////////////////////////////////////////////////////
// NoeudSi
////////////////////////////////////////////////////////////////////////////////

NoeudSi::NoeudSi(int ligne, int colonne, Noeud* condition, Noeud* instruction, Noeud* suivant, bool si) : Noeud(ligne, colonne) {
  this->condition = condition;
  this->instruction = instruction;
  this->suivant = suivant;
  this->si = si;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudSi::getValeur() {
  int valeur=0;
  if(condition->getValeur()) {
	valeur = instruction->getValeur();
  } else if(suivant != NULL) {
	valeur = suivant->getValeur();
  }
  return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSi::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "NoeudSi - pour applique a : " << endl;
  instruction->afficher(indentation+1);  // on affiche fils gauche et fils droit
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSi::afficherPhp() {
	if(si) {
		cout << "if(";
		condition->afficherPhp();
		cout << ") {" << endl;
		instruction->afficherPhp();
		cout << "}" << endl;
	} else if(!si) {
		cout << "else if(";
		condition->afficherPhp();
		cout << ") {" << endl;
		instruction->afficherPhp();
		cout << "}" << endl;
	}
	if(suivant != NULL) {	
		if(typeid(*suivant) ==  typeid(NoeudSeqInst)) {
			cout << "else {" << endl;
			suivant->afficherPhp();
			cout << "}" << endl;
		} else {
			suivant->afficherPhp();
		}
	}
}
////////////////////////////////////////////////////////////////////////////////
// NoeudLire
////////////////////////////////////////////////////////////////////////////////

NoeudLire::NoeudLire(int ligne, int colonne, Noeud* var) : Noeud(ligne, colonne) {
  this->var = var; 
}

////////////////////////////////////////////////////////////////////////////////
int NoeudLire::getValeur() {
  int valeur=0;
  cin >> valeur;
  ((SymboleValue*)var)->setValeur(valeur);
  return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudLire::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "NoeudLire - pour applique a : " << endl;
  var->afficher(indentation+1);  // on affiche fils gauche et fils droit
}

////////////////////////////////////////////////////////////////////////////////
void NoeudLire::afficherPhp() {
	var->afficherPhp();
	cout << " = fgets(STDIN);" << endl;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudEcrire
////////////////////////////////////////////////////////////////////////////////

NoeudEcrire::NoeudEcrire(int ligne, int colonne, bool chaine, Noeud* var) : Noeud(ligne, colonne) {
  	this->var = var; 
	this->chaine=chaine;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudEcrire::getValeur() {
  int valeur=0;
  if(this->chaine) {
	cout << ((SymboleValue*)var)->getChaine();
  } else {
	valeur = var->getValeur();
	cout << valeur;	
  }
  return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudEcrire::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "NoeudEcrire - pour applique a : " << endl;
  var->afficher(indentation+1);  // on affiche fils gauche et fils droit
}

////////////////////////////////////////////////////////////////////////////////
void NoeudEcrire::afficherPhp() {
	cout << "echo ";
	var->afficherPhp();
	cout << ";" << endl;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudChoisir
////////////////////////////////////////////////////////////////////////////////

NoeudChoisir::NoeudChoisir(int ligne, int colonne, Noeud* choisir, Noeud* cas, Noeud* instruction, Noeud* suivant) : Noeud(ligne, colonne) {
  this->choisir = choisir;
  this->cas = cas;
  this->instruction = instruction;
  this->suivant = suivant;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudChoisir::getValeur() {
  int valeur=0;

  int val = choisir->getValeur();

  if(val == cas->getValeur()){
	valeur = instruction->getValeur();
  } else if(suivant != NULL) {
	if(typeid(*suivant) ==  typeid(NoeudCas)) {
		valeur =  suivant->getValeurCase(val);
	} else {
		valeur = suivant->getValeur();
	}
  }

  return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudChoisir::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "NoeudChoisir - pour applique a : " << endl;
  instruction->afficher(indentation+1);  // on affiche fils gauche et fils droit
}


////////////////////////////////////////////////////////////////////////////////
void NoeudChoisir::afficherPhp() {
	cout << "switch(";
	choisir->afficherPhp();
	cout << ") {" << endl;
	cout << "case ";
	cas->afficherPhp();
	cout << " :" << endl;
	instruction->afficherPhp();
	cout << "break;" << endl;
	if(suivant != NULL) {	
		if(typeid(*suivant) ==  typeid(NoeudSeqInst)) {
			cout << "default :" << endl;
			suivant->afficherPhp();
			cout << "}" << endl;
		} else {
			suivant->afficherPhp();
		}
	} else {
		cout << "}" << endl;
	}
}

////////////////////////////////////////////////////////////////////////////////
// NoeudCas
////////////////////////////////////////////////////////////////////////////////

NoeudCas::NoeudCas(int ligne, int colonne, Noeud* cas, Noeud* instruction, Noeud* suivant) : Noeud(ligne, colonne) {
  this->cas = cas;
  this->instruction = instruction;
  this->suivant = suivant;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudCas::getValeur() {
  int valeur=0;
  return valeur;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudCas::getValeurCase(int val) {
  int valeur=0;
	
  if(val == cas->getValeur()){
	valeur = instruction->getValeur();
  } else if(suivant != NULL) {
	if(typeid(*suivant) ==  typeid(NoeudCas)) {
		valeur =  suivant->getValeurCase(val);
	} else {
		valeur = suivant->getValeur();
	}
  }

  return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudCas::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "NoeudCas - pour applique a : " << endl;
  instruction->afficher(indentation+1);  // on affiche fils gauche et fils droit
}

////////////////////////////////////////////////////////////////////////////////
void NoeudCas::afficherPhp() {
	cout << "case ";
	cas->afficherPhp();
	cout << " :" << endl;
	instruction->afficherPhp();
	cout << "break;" << endl;
	if(suivant != NULL) {	
		if(typeid(*suivant) ==  typeid(NoeudSeqInst)) {
			cout << "default :" << endl;
			suivant->afficherPhp();
			cout << "}" << endl;
		} else {
			suivant->afficherPhp();
		}
	} else {
		cout << "}" << endl;
	}
}